/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.guacamole.rest.session;

import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.GuacamoleSession;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.auth.AuthenticationService;
import org.apache.guacamole.rest.tunnel.TunnelCollectionResource;
import org.apache.guacamole.rest.tunnel.TunnelCollectionResourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * A REST resource which exposes all data associated with a Guacamole user's
 * session via the underlying UserContexts.
 */
@Produces(MediaType.APPLICATION_JSON)
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED})
public class SessionResource {

    /**
     * Logger for this class.
     */
    private static final Logger logger = LoggerFactory.getLogger(SessionResource.class);

    /**
     * The GuacamoleSession being exposed by this SessionResource.
     */
    private final GuacamoleSession session;

    /**
     * The Guacamole server environment.
     */
    private Environment environment;

    /**
     * The authentication token associated with the GuacamoleSession being
     * exposed by this SessionResource.
     */
    private final String token;

    /**
     * Service for authenticating users and managing their Guacamole sessions.
     */
    @Inject
    private AuthenticationService authenticationService;

    /**
     * Factory for creating UserContextResources which expose a given
     * UserContext.
     */
    @Inject
    private UserContextResourceFactory userContextResourceFactory;

    /**
     * Factory for creating instances of resources which represent the
     * collection of tunnels within a GuacamoleSession.
     */
    @Inject
    private TunnelCollectionResourceFactory tunnelCollectionResourceFactory;

    /**
     * Creates a new SessionResource which exposes the data within the given
     * GuacamoleSession.
     *
     * @param token   The authentication token associated with the given session.
     * @param session The GuacamoleSession which should be exposed through this
     *                SessionResource.
     */
    @AssistedInject
    public SessionResource(@Assisted String token, @Assisted GuacamoleSession session) {
        this.token = token;
        this.session = session;
    }

    /**
     * Retrieves a resource representing the UserContext associated with the
     * AuthenticationProvider having the given identifier.
     *
     * @param authProviderIdentifier The unique identifier of the AuthenticationProvider associated with
     *                               the UserContext being retrieved.
     * @return A resource representing the UserContext associated with the
     * AuthenticationProvider having the given identifier.
     * @throws GuacamoleException If the AuthenticationProvider identifier is invalid.
     */
    @Path("data/{dataSource}")
    public UserContextResource getUserContextResource(
            @PathParam("dataSource") String authProviderIdentifier)
            throws GuacamoleException {

        // Pull UserContext defined by the given auth provider identifier
        UserContext userContext = session.getUserContext(authProviderIdentifier);

        // Return a resource exposing the retrieved UserContext
        return userContextResourceFactory.create(userContext);

    }

    /**
     * Returns the arbitrary REST resource exposed by the UserContext
     * associated with the AuthenticationProvider having the given identifier.
     *
     * @param authProviderIdentifier The unique identifier of the AuthenticationProvider associated with
     *                               the UserContext whose arbitrary REST service is being retrieved.
     * @return The arbitrary REST resource exposed by the UserContext exposed by
     * this UserContextresource.
     * @throws GuacamoleException If no such resource could be found, or if an error occurs while
     *                            retrieving that resource.
     */
    @Path("ext/{dataSource}")
    public Object getExtensionResource(
            @PathParam("dataSource") String authProviderIdentifier)
            throws GuacamoleException {

        // Pull UserContext defined by the given auth provider identifier
        UserContext userContext = session.getUserContext(authProviderIdentifier);

        // Pull resource from user context
        Object resource = userContext.getResource();
        if (resource != null)
            return resource;

        // UserContext-specific resource could not be found
        throw new GuacamoleResourceNotFoundException("No such resource.");

    }

    /**
     * Retrieves a resource representing all tunnels associated with session
     * exposed by this SessionResource.
     *
     * @return A resource representing all tunnels associated with the
     * AuthenticationProvider having the given identifier.
     */
    @Path("tunnels")
    public TunnelCollectionResource getTunnelCollectionResource() {
        return tunnelCollectionResourceFactory.create(session);
    }

    @POST
    @Path("config/{id}")
    public Object config(@PathParam("id") String id,
                          @FormParam("path") String path) {
        Map<String, Object> map = new HashMap<>();

        map.put("message", "添加成功");
        return map;
    }

    /**
     * 删除远程服务器文件
     *
     * @param id 服务器连接ID
     */
    @POST
    @Path("file-del/{id}")
    public Object fileDel(@PathParam("id") String id,
                          @FormParam("path") String path) {
        Map<String, Object> map = new HashMap<>();
        logger.info("进入文件删除接口 id：{},path：{}", id, path);
        if (path == null || path.length() <= 0) {
            logger.info("文件传参数错误");
            return "文件传参数错误";
        }
        String[] split = path.split("/");
        if (!split[split.length - 1].contains(".")) {
            logger.info("所传不为文件，无法删除!!");
            return "所传不为文件，无法删除!!";
        }
        environment = LocalEnvironment.getInstance();
        SHHUser shhUser = this.readProperties(id, environment.getGuacamoleHome() + "/guacamole.properties");
        if (null == shhUser) {
            map.put("message", "删除失败");
            return map;
        }
        // SSH连接用户名
        String user = shhUser.getUserName();
        // SSH连接密码
        String password = shhUser.getPassWord();
        // 服务器地址
        String host = shhUser.getHost();
        // SSH端口，默认是22
        int port = shhUser.getPort();
        // 需要执行的命令
        String command = "rm -rf " + path;
        logger.info("传输的文件path为:" + path);
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(user, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();

            // 打开一个执行通道
            Channel channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(command);
            channel.setInputStream(null);
            ((ChannelExec) channel).setErrStream(System.err);

            java.io.InputStream in = channel.getInputStream();
            channel.connect();

            // 读取命令的输出
            byte[] tmp = new byte[1024];
            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) break;
                    System.out.print(new String(tmp, 0, i));
                }
                if (channel.isClosed()) {
                    System.out.println("exit-status: " + channel.getExitStatus());
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (Exception ee) {
                }
            }
            channel.disconnect();
            session.disconnect();

        } catch (Exception e) {
            e.printStackTrace();
        }
        map.put("message", "删除成功");
        return map;
    }

    /**
     * 获取连接对象
     *
     * @param index 索引
     * @param path  路径
     */
    public SHHUser readProperties(String index, String path) {
        try (InputStream inputStream = new BufferedInputStream(new FileInputStream(path))) {
            Properties properties = new Properties();
            properties.load(inputStream);
            properties.list(System.out);
            logger.info("==============================================");
            String hostname = properties.getProperty(index + ".hostname");
            String username = properties.getProperty(index + ".username");
            String password = properties.getProperty(index + ".password");
            String port = properties.getProperty(index + ".port");
            SHHUser shhUser = new SHHUser(hostname, username, password, Integer.parseInt(port));
            return shhUser;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    class SHHUser {
        public SHHUser(String host, String userName, String passWord, Integer port) {
            this.host = host;
            this.userName = userName;
            this.passWord = passWord;
            this.port = port;
        }

        private String host;
        private String userName;
        private String passWord;
        private Integer port;

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public String getPassWord() {
            return passWord;
        }

        public void setPassWord(String passWord) {
            this.passWord = passWord;
        }

        public Integer getPort() {
            return port;
        }

        public void setPort(Integer port) {
            this.port = port;
        }
    }

    /**
     * Invalidates the GuacamoleSession exposed by this SessionResource,
     * including the associated authentication token.
     *
     * @throws GuacamoleException If the authentication token originally provided when this
     *                            SessionResource was created no longer exists.
     */
    @DELETE
    public void invalidate() throws GuacamoleException {

        // Invalidate session, if it exists
        if (!authenticationService.destroyGuacamoleSession(token))
            throw new GuacamoleResourceNotFoundException("No such token.");

    }

}
